package my;

import my.concurrent.DefaultThreadFactory;
import my.concurrent.ThreadPerTaskExecutor;
import my.socket.Socket;

import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author fengbo
 */
public class EpollEventLoopGroup {

    private static final int DEFAULT_EVENT_LOOP_THREADS;

    private final EpollEventLoop[] children;
    private final ExecutorChooser chooser;

    static {
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, Runtime.getRuntime().availableProcessors());
    }

    public EpollEventLoopGroup() {
        this(DEFAULT_EVENT_LOOP_THREADS);
    }

    public EpollEventLoopGroup(int nThreads) {
        this(nThreads, new ThreadPerTaskExecutor(new DefaultThreadFactory()));
    }

    public EpollEventLoopGroup(int nThreads, Executor executor) {
        children = new EpollEventLoop[nThreads];
        chooser = new ExecutorChooser(children);
        for (int i = 0; i < nThreads; i++) {
            boolean success = false;
            try {
                children[i] = newChild(executor);
                success = true;
            } catch (Exception e) {
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                if (!success) {
                    // 关闭资源
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdown();
                    }
                }
            }
        }
    }

    public void register(Socket socket) {
        socket.register(next());
    }

    private EpollEventLoop next() {
        return chooser.next();
    }

    private EpollEventLoop newChild(Executor executor) {
        return new EpollEventLoop(executor);
    }

    private static final class ExecutorChooser {
        private final AtomicInteger idx = new AtomicInteger();
        private final EpollEventLoop[] executors;

        ExecutorChooser(EpollEventLoop[] executors) {
            this.executors = executors;
        }

        public EpollEventLoop next() {
            return executors[Math.abs(idx.incrementAndGet() % executors.length)];
        }
    }
}
